package models

import (
	"errors"
	"fmt"
	"go_pure_admin_api/dto"
	"go_pure_admin_api/global"
	"gorm.io/gorm"
)

type Menus struct {
	gorm.Model
	Name       string `gorm:"comment:路由名称;size:50" json:"name" validate:"required" form:"name"`
	Title      string `gorm:"comment:菜单标题;size:50" json:"title" form:"title"`                   // 菜单标题
	Type       int8   `gorm:"comment:菜单类型:0菜单1按钮2API;" json:"menuType" form:"menuType"`         // 菜单/按钮/API
	Path       string `gorm:"comment:路由路径;size:200" json:"path" form:"path"`                    // 路由路径
	Component  string `gorm:"comment:组件路径;size:200" json:"component" form:"component"`          // 组件路径
	Method     string `gorm:"comment:请求方式;size:10" json:"method" form:"method"`                 // 请求方式
	Auths      string `gorm:"comment:权限标识;size:100" json:"auths" form:"auths"`                  // 权限标识
	Icon       string `gorm:"comment:图标;size:50" json:"icon" form:"icon"`                       // 图标
	Rank       int    `gorm:"comment:排序;default:0;column:rank" json:"rank" form:"rank"`         // 排序字段
	ShowLink   bool   `gorm:"comment:是否隐藏;default:false" json:"showLink" form:"showLink"`       // 是否在菜单中显示
	ShowParent bool   `gorm:"comment:是否显示父级;default:false" json:"showParent" form:"showParent"` // 是否显示父级菜单
	// 使用 constraint 标签定义级联删除
	ParentID *uint   `gorm:"column:parent_id;comment:父级ID;index;constraint:OnDelete:CASCADE,OnUpdate:RESTRICT" json:"parentId" form:"parentId"` // 父级ID
	Parent   *Menus  `gorm:"foreignKey:ParentID;references:ID" json:"parent"`                                                                   // 父级菜单
	Children []Menus `gorm:"foreignKey:ParentID;references:ID" json:"children"`                                                                 // 子菜单
	Roles    []Roles `gorm:"many2many:roles_menus;" json:"roles"`                                                                               // 关联角色表
}

func init() {
	err := global.DB.AutoMigrate(&Menus{})
	if err != nil {
		global.Logger.Errorf("自动迁移菜单表发生错误: %v", err)
		return
	}
	// 强制创建级联约束
	global.DB.Exec("ALTER TABLE menus DROP FOREIGN KEY fk_menus_children")
	global.DB.Exec(`
        ALTER TABLE menus 
        ADD CONSTRAINT fk_menus_children 
        FOREIGN KEY (parent_id) REFERENCES menus(id) 
        ON DELETE CASCADE
    `)
}

// GetAllMenus 获取所有菜单
func GetAllMenus() (menus []Menus, err error) {
	// 这里有个坑 rank是MySQL的保留字，不能直接使用 所以需要使用反引号包裹
	err = global.DB.Model(Menus{}).Where("type = ?", 0).Find(&menus).Error
	return
}

// GetAllMenusList 获取所有Menus列表
func (m *Menus) GetAllMenusList() (menus []Menus, err error) {
	// 这里有个坑 rank是MySQL的保留字，不能直接使用 所以需要使用反引号包裹
	err = global.DB.Model(&menus).Order("`rank` asc").Find(&menus).Error
	return
}

// BuildMenuTree 构建菜单路由树
func BuildMenuTree(menusDTO *dto.MenuDto, menus []Menus) []*dto.MenuDto {
	// 创建一个映射来存储已处理的菜单ID，防止重复处理
	processedIDs := make(map[uint]bool)
	return buildMenuTreeWithProcessedIDs(menusDTO, menus, processedIDs)
}

// buildMenuTreeWithProcessedIDs 递归构建菜单路由树并跟踪已处理的ID
func buildMenuTreeWithProcessedIDs(menusDTO *dto.MenuDto, menus []Menus, processedIDs map[uint]bool) []*dto.MenuDto {
	var children []*dto.MenuDto
	for _, menu := range menus {
		// 如果当前菜单的ParentID等于当前菜单的ID，且该菜单未被处理过
		if (menu.ParentID != nil && menusDTO.ID != 0 && *menu.ParentID == menusDTO.ID) ||
			(menu.ParentID == nil && menusDTO.ID == 0) {
			// 检查是否已经处理过此菜单
			if processedIDs[menu.ID] {
				continue // 已处理过，跳过
			}

			// 标记为已处理
			processedIDs[menu.ID] = true

			// 构建子菜单路由对象
			childMenuRoute := &dto.MenuDto{
				ID:   menu.ID,
				Path: menu.Path,
				Name: menu.Name,
				Meta: dto.Meta{
					Title:    menu.Title,    // 菜单标题
					Icon:     menu.Icon,     // 菜单图标
					Rank:     menu.Rank,     // 菜单排序
					ShowLink: menu.ShowLink, // 是否在菜单中显示
				},
			}

			// 递归构建子菜单的子菜单
			grandChildren := buildMenuTreeWithProcessedIDs(childMenuRoute, menus, processedIDs)

			// 只有当存在孙子菜单时，才设置 Children 字段
			if len(grandChildren) > 0 {
				childMenuRoute.Children = grandChildren
			}

			// 将构建好的子菜单添加到临时列表中
			children = append(children, childMenuRoute)
		}
	}

	// 只有当存在子菜单时，才设置父级的 Children 字段
	if len(children) > 0 {
		menusDTO.Children = children
	}

	// 返回子菜单列表
	return children
}

// CreateMenu 创建菜单
func (m *Menus) CreateMenu(data dto.CreateMenuDto) error {
	m.Name = data.Name
	m.Title = data.Title
	m.Type = data.Type
	m.Path = data.Path
	m.Icon = data.Icon
	m.Rank = data.Rank
	m.ShowLink = data.ShowLink
	m.ShowParent = data.ShowParent
	m.Auths = data.Auths
	m.Method = data.Method

	// 处理 ParentID
	if data.ParentID == 0 {
		m.ParentID = nil // 设置为 nil 表示没有父级
	} else {
		// 验证父级菜单是否存在
		parentMenu := &Menus{}
		if err := global.DB.First(parentMenu, data.ParentID).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return fmt.Errorf("父级菜单不存在，ID: %d", data.ParentID)
			}
			return fmt.Errorf("查询父级菜单时发生错误: %w", err)
		}
		m.ParentID = &data.ParentID // 设置父级ID
	}

	if err := global.DB.Create(m).Error; err != nil {
		return fmt.Errorf("创建菜单失败: %w", err)
	}
	return nil
}

// UpdateMenu 更新菜单
func (m *Menus) UpdateMenu(data dto.UpdateMenuDto) error {
	if err := global.DB.First(m, data.ID).Error; err != nil {
		return fmt.Errorf("菜单不存在: %w", err)
	}

	// 更新基本字段
	m.Name = data.Name
	m.Title = data.Title
	m.Type = data.Type
	m.Path = data.Path
	m.Icon = data.Icon
	m.Rank = data.Rank
	m.ShowLink = data.ShowLink
	m.ShowParent = data.ShowParent
	m.Method = data.Method
	m.Auths = data.Auths

	// 处理父级关系更新
	if data.ParentID == 0 {
		// 设置为顶级菜单
		m.ParentID = nil
	} else {
		// 验证父级菜单是否存在
		parentMenu := &Menus{}
		if err := global.DB.First(parentMenu, data.ParentID).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return fmt.Errorf("父级菜单不存在，ID: %d", data.ParentID)
			}
			return fmt.Errorf("查询父级菜单时发生错误: %w", err)
		}

		// 检查是否会形成循环引用
		if err := m.checkCircularReference(data.ParentID); err != nil {
			return err
		}

		m.ParentID = &data.ParentID
	}

	if err := global.DB.Save(m).Error; err != nil {
		return fmt.Errorf("更新菜单失败: %w", err)
	}
	return nil
}

// checkCircularReference 检查是否会形成循环引用
func (m *Menus) checkCircularReference(parentID uint) error {
	if m.ID == parentID {
		return errors.New("不能将菜单设置为自己的父级")
	}

	// 递归检查父级链
	currentParentID := parentID
	for currentParentID != 0 {
		var parent Menus
		if err := global.DB.First(&parent, currentParentID).Error; err != nil {
			break
		}

		if parent.ParentID != nil && *parent.ParentID == m.ID {
			return errors.New("不能形成循环引用")
		}

		if parent.ParentID == nil {
			break
		}
		currentParentID = *parent.ParentID
	}

	return nil
}

// DeleteMenuByID 删除菜单
func (m *Menus) DeleteMenuByID(menuID uint) error {
	return global.DB.Transaction(func(tx *gorm.DB) error {
		// 收集所有需要删除的菜单ID（包括子菜单），用于清理关联表
		var idsToDelete []uint
		if err := collectMenuIDs(menuID, &idsToDelete); err != nil {
			return fmt.Errorf("收集菜单ID失败: %w", err)
		}

		// 清理多对多关联表（级联删除不会自动处理多对多关系）
		if len(idsToDelete) > 0 {
			if err := tx.Table("roles_menus").
				Where("menus_id IN ?", idsToDelete).
				Delete(nil).Error; err != nil {
				return fmt.Errorf("删除菜单角色关联失败: %w", err)
			}
		}

		// 执行主菜单删除，由于设置了 ON DELETE CASCADE，所有子菜单会自动删除
		if err := tx.Delete(m, menuID).Error; err != nil {
			return fmt.Errorf("删除菜单失败: %w", err)
		}
		return nil
	})
}

// collectMenuIDs 递归收集所有子菜单的ID
// 这个函数用于获取需要清理关联表的所有菜单ID
func collectMenuIDs(id uint, ids *[]uint) error {
	*ids = append(*ids, id)

	var children []Menus
	if err := global.DB.Where("parent_id = ?", id).Find(&children).Error; err != nil {
		return fmt.Errorf("查询子菜单失败: %w", err)
	}

	for _, child := range children {
		if err := collectMenuIDs(child.ID, ids); err != nil {
			return err
		}
	}
	return nil
}

func GetMenusById(ids []uint) (menus []Menus, err error) {
	if len(ids) == 0 {
		return
	}
	if err = global.DB.Where("id IN ?", ids).Find(&menus).Error; err != nil {
		return nil, err
	}
	return

}
func GetAPIsById(ids []uint) (API []Menus, err error) {
	if len(ids) == 0 {
		return
	}
	if err = global.DB.Where("parent_id IN ?", ids).Where("type = ?", 2).Find(&API).Error; err != nil {
		return nil, err
	}
	return
}
